package com.yibo.redis.util.lock;


import com.yibo.support.context.SpringContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.concurrent.TimeUnit;

/**
 * redis分布式锁实现
 *
 * 锁本身并不是线程安全的，使用请在函数内部创建新的锁实例使用。
 *
 * @author zhanglei
 */
@SuppressWarnings("unchecked")
public class RedisLock implements YBLock{

    private static Logger logger = LoggerFactory.getLogger(RedisLock.class);

    /**
     * redisTemplate
     */
    private RedisTemplate redisTemplate;

    /**
     * 获取锁失败，回调
     */
    private LockBack lockBack;

    /**
     * 锁字符串
     */
    private String lockKey;

    /**
     * 锁过期时间  默认10秒  放置程序故障导致的锁永久不释放 单位毫秒
     */
    private  int  expireTime = 1000 * 10;

    public RedisLock(String lockKey){
        this.lockKey = lockKey;
        synchronized (RedisLock.class){
            if(redisTemplate == null){
                redisTemplate = SpringContext.getBean(StringRedisTemplate.class);
            }
        }
    }

    public RedisLock(String lockKey,int expireTime){
        this(lockKey);
        this.expireTime = expireTime;
    }

    public RedisLock(String lockKey,int expireTime,LockBack lockBack){
        this(lockKey,expireTime);
        this.lockBack = lockBack;
    }

    public RedisLock(String lockKey, LockBack lockBack){
        this(lockKey);
        this.lockBack = lockBack;
    }

    /**
     * 获取锁
     */
    @Override
    public boolean acquire() {

        //过期时间值
        String  expire = Long.toString(System.currentTimeMillis() + this.expireTime);

        try {
            while (true) {

                //在redis中设置值
                boolean acq = redisTemplate.opsForValue().setIfAbsent(this.lockKey, expire);

                //设置成功
                if (acq) {
                    logger.info("get redis lock success!");
                    //设置过期时间
                    redisTemplate.expire(this.lockKey, expireTime, TimeUnit.MILLISECONDS);
                    return true;
                }

                //如果获取锁失败，调用回调
                if (this.lockBack != null) {
                    this.lockBack.fallback(this);
                    return false;
                }


                //睡眠100毫秒
                Thread.sleep(100);
            }
        }catch (Exception e){
            logger.error(e.getMessage(),e);
        }
        return false;
    }

    /**
     * 释放锁
     */
    @Override
    public void release() {
        this.redisTemplate.delete(this.lockKey);
    }

    public String getLockKey() {
        return lockKey;
    }

    public void setLockKey(String lockKey) {
        this.lockKey = lockKey;
    }

    public int getExpireTime() {
        return expireTime;
    }

    public void setExpireTime(int expireTime) {
        this.expireTime = expireTime;
    }

}
